package dev.jukomu.jmcomic.util;

import dev.jukomu.common.util.file.FileUtils;
import dev.jukomu.common.util.net.IResponse;
import dev.jukomu.jmcomic.config.JmMagicConstants;
import dev.jukomu.jmcomic.entity.JmImageDetail;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 对应Python的JmImageTool，提供禁漫图片处理工具。
 * 包括图片保存、解密、打开等。
 */
public class JmImageUtils {

    /**
     * 保存响应的图片内容到文件。
     * 如果需要转换图片格式（例如JPG转PNG），则指定needConvert为true。
     * 如果不需要转换，直接保存字节流效率更高。
     *
     * @param resp        响应对象（包含图片内容）
     * @param filepath    图片保存路径
     * @param needConvert 是否需要转换图片格式
     * @throws IOException 如果保存失败
     */
    public static void saveRespImg(IResponse resp, String filepath, boolean needConvert) throws IOException {
        if (needConvert) {
            saveImage(openImage(resp.getContent()), filepath);
        } else {
            saveDirectly(resp.getContent(), filepath);
        }
    }

    /**
     * 保存BufferedImage对象到文件。
     *
     * @param image    BufferedImage对象
     * @param filepath 保存文件路径
     * @throws IOException 如果保存失败
     */
    public static void saveImage(BufferedImage image, String filepath) throws IOException {
        String suffix = FileUtils.getFileSuffix(filepath, true); // 获取不带点的后缀
        // ImageIO.write需要知道图片格式，例如 "png", "jpg", "webp"
        // 从后缀中提取格式
        String formatName = suffix.isEmpty() ? "png" : suffix; // 默认PNG
        ImageIO.write(image, formatName, new File(filepath));
    }

    /**
     * 直接保存字节数组到文件。
     *
     * @param content  字节数组（图片内容）
     * @param filepath 文件路径
     * @throws IOException 如果保存失败
     */
    public static void saveDirectly(byte[] content, String filepath) throws IOException {
        FileUtils.getDirPath(filepath, true); // 确保父目录存在
        Files.write(Paths.get(filepath), content);
    }

    /**
     * 解密图片并保存。
     *
     * @param num             分割数，可由calculateSegmentationNum计算
     * @param imgSrc          原始图片（BufferedImage）
     * @param decodedSavePath 解密图片的保存路径
     * @throws IOException 如果解密或保存失败
     */
    public static void decodeAndSave(int num, BufferedImage imgSrc, String decodedSavePath) throws IOException {
        // 无需解密，直接保存
        if (num == 0) {
            saveImage(imgSrc, decodedSavePath);
            return;
        }

        int width = imgSrc.getWidth();
        int height = imgSrc.getHeight();

        // 创建新的解密图片
        BufferedImage imgDecode = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        int segmentHeight = height / num; // 每个分割块的理论高度
        int remainderHeight = height % num; // 剩余的像素高度

        // Python的逻辑是：
        // over = h % num
        // for i in range(num):
        //     move = floor(h / num)
        //     y_src = h - (move * (i + 1)) - over
        //     y_dst = move * i
        //     if i == 0:
        //         move += over
        //     else:
        //         y_dst += over
        //
        // 这是一个反向拼接的过程，从原图底部开始取块，拼接到新图顶部。
        // 原图被分成 num 块，最后一块包含所有剩余像素。
        // 第i块 (从0开始) 对应的原图y坐标范围：
        // 底部第一块 (i=0)：y_src = h - (move * 1) - over --> h - segmentHeight - remainderHeight
        //               move = segmentHeight + remainderHeight
        //               y_dst = 0
        // 其他块 (i>0)：y_src = h - (move * (i+1)) - over
        //               move = segmentHeight
        //               y_dst = segmentHeight * i + remainderHeight

        for (int i = 0; i < num; i++) {
            int currentSegmentHeight = segmentHeight;
            int currentSrcY;
            int currentDstY;

            if (i == 0) { // 最底部一块
                currentSegmentHeight += remainderHeight;
                currentSrcY = height - currentSegmentHeight;
                currentDstY = 0;
            } else {
                currentSrcY = height - (segmentHeight * (i + 1)) - remainderHeight;
                currentDstY = (segmentHeight * i) + remainderHeight;
            }

            // 确保截取范围在图片内
            currentSrcY = Math.max(0, currentSrcY);
            int srcHeightToCopy = Math.min(currentSegmentHeight, height - currentSrcY);

            // 截取原图的一部分
            BufferedImage subImage = imgSrc.getSubimage(0, currentSrcY, width, srcHeightToCopy);
            // 将截取的部分绘制到解密图片上
            imgDecode.getGraphics().drawImage(subImage, 0, currentDstY, null);
        }

        // 保存到新的解密文件
        saveImage(imgDecode, decodedSavePath);
    }

    /**
     * 打开图片文件或字节流为BufferedImage。
     *
     * @param fp 文件路径字符串或字节数组
     * @return BufferedImage对象
     * @throws IOException 如果打开失败
     */
    public static BufferedImage openImage(Object fp) throws IOException {
        if (fp instanceof String) {
            return ImageIO.read(new File((String) fp));
        } else if (fp instanceof byte[]) {
            return ImageIO.read(new ByteArrayInputStream((byte[]) fp));
        } else {
            throw new IllegalArgumentException("Unsupported image input type: " + fp.getClass().getName());
        }
    }

    /**
     * 获得图片分割数。
     *
     * @param scrambleId 图片所属章节的解密ID
     * @param aid        本子ID或章节ID
     * @param filename   图片文件名（不含后缀）
     * @return 图片分割数
     */
    public static int getNum(String scrambleId, String aid, String filename) {
        int scramble = Integer.parseInt(scrambleId);
        int albumId = Integer.parseInt(aid);

        if (albumId < scramble) {
            return 0; // 无需解密
        } else if (albumId < JmMagicConstants.SCRAMBLE_268850) {
            return 10;
        } else {
            int x = (albumId < JmMagicConstants.SCRAMBLE_421926) ? 10 : 8;
            String s = aid + filename; // 拼接ID和文件名
            String md5Hex = md5Hex(s);
            int num = (int) md5Hex.charAt(md5Hex.length() - 1); // 取MD5最后一字符的ASCII值
            num %= x;
            num = num * 2 + 2;
            return num;
        }
    }

    /**
     * 获得图片分割数，通过URL。
     *
     * @param scrambleId 解密ID
     * @param url        图片URL
     * @return 图片分割数
     */
    public static int getNumByUrl(String scrambleId, String url) {
        String aid = JmcomicText.parseToJmId(url);
        String filename = FileUtils.getFileName(url, true); // 获取不带后缀的文件名
        return getNum(scrambleId, aid, filename);
    }

    /**
     * 获得图片分割数，通过JmImageDetail。
     *
     * @param detail JmImageDetail实例
     * @return 图片分割数
     */
    public static int getNumByDetail(JmImageDetail detail) {
        return getNum(detail.getScrambleId(), detail.getAid(), detail.getImgFileName());
    }

    /**
     * 计算字符串的MD5哈希值（十六进制表示）。
     *
     * @param key 待哈希的字符串
     * @return MD5哈希值的十六进制字符串
     * @throws RuntimeException 如果MD5算法不可用
     */
    private static String md5Hex(String key) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(key.getBytes(java.nio.charset.StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
    }

    // 私有构造函数，防止实例化
    private JmImageUtils() {
    }
}